// Copyright 2018 The Grafeas Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package vulnerability implements functions to validate that the fields of vulnerability entities
// being passed into the API meet our requirements.
package vulnerability

import (
	"errors"
	"fmt"

	"github.com/grafeas/grafeas/go/v1beta1/api/validators/package"
	vulnpb "github.com/grafeas/grafeas/proto/v1beta1/vulnerability_go_proto"
)

// ValidateVulnerability validates that a vulnerability has all its required fields filled in.
func ValidateVulnerability(v *vulnpb.Vulnerability) []error {
	errs := []error{}

	for i, detail := range v.GetDetails() {
		if detail == nil {
			errs = append(errs, fmt.Errorf("details[%d] detail cannot be null", i))
		} else {
			for _, err := range validateVulnerabilityDetail(detail) {
				errs = append(errs, fmt.Errorf("details[%d].%s", i, err))
			}
		}
	}

	return errs
}

func validateVulnerabilityDetail(vd *vulnpb.Vulnerability_Detail) []error {
	errs := []error{}

	if vd.GetCpeUri() == "" {
		errs = append(errs, errors.New("cpe_uri is required"))
	}
	if vd.GetPackage() == "" {
		errs = append(errs, errors.New("package is required"))
	}

	if ver := vd.GetMinAffectedVersion(); ver != nil {
		for _, err := range pkg.ValidateVersion(ver) {
			errs = append(errs, fmt.Errorf("min_affected_version.%s", err))
		}
	}
	if ver := vd.GetMaxAffectedVersion(); ver != nil {
		for _, err := range pkg.ValidateVersion(ver) {
			errs = append(errs, fmt.Errorf("max_affected_version.%s", err))
		}
	}
	if fl := vd.GetFixedLocation(); fl != nil {
		for _, err := range validateVulnerabilityLocation(fl) {
			errs = append(errs, fmt.Errorf("fixed_location.%s", err))
		}
	}

	return errs
}

func validateVulnerabilityLocation(vl *vulnpb.VulnerabilityLocation) []error {
	errs := []error{}

	if vl.GetCpeUri() == "" {
		errs = append(errs, errors.New("cpe_uri is required"))
	}
	if vl.GetPackage() == "" {
		errs = append(errs, errors.New("package is required"))
	}
	if ver := vl.GetVersion(); ver == nil {
		errs = append(errs, errors.New("version is required"))
	} else {
		for _, err := range pkg.ValidateVersion(ver) {
			errs = append(errs, fmt.Errorf("version.%s", err))
		}
	}

	return errs
}

// ValidateDetails validates that a details has all its required fields filled in.
func ValidateDetails(d *vulnpb.Details) []error {
	errs := []error{}

	if pkgIssue := d.GetPackageIssue(); pkgIssue == nil {
		errs = append(errs, errors.New("package_issue is required"))
	} else if len(pkgIssue) == 0 {
		errs = append(errs, errors.New("package_issue requires at least 1 element"))
	} else {
		for i, p := range pkgIssue {
			if p == nil {
				errs = append(errs, fmt.Errorf("package_issue[%d] package issue cannot be null", i))
			} else {
				for _, err := range validatePackageIssue(p) {
					errs = append(errs, fmt.Errorf("package_issue[%d].%s", i, err))
				}
			}
		}
	}

	return errs
}

func validatePackageIssue(p *vulnpb.PackageIssue) []error {
	errs := []error{}

	if al := p.GetAffectedLocation(); al == nil {
		errs = append(errs, errors.New("affected_location is required"))
	} else {
		for _, err := range validateVulnerabilityLocation(al) {
			errs = append(errs, fmt.Errorf("affected_location.%s", err))
		}
	}

	if fl := p.GetFixedLocation(); fl != nil {
		for _, err := range validateVulnerabilityLocation(fl) {
			errs = append(errs, fmt.Errorf("fixed_location.%s", err))
		}
	}

	return errs
}
